float4 vfViewPos				: PB_CameraPos;
float4 vfParams					: PB_VolumetricFogParams;
float4 vfRampParams			: PB_VolumetricFogRampParams;
float3 vfSunDir					: PB_VolumetricFogSunDir;

float3 vfColGradBase		: PB_FogColGradColBase;
float3 vfColGradDelta		: PB_FogColGradColDelta;
float4 vfColGradParams	: PB_FogColGradParams;
float4 vfColGradRadial	: PB_FogColGradRadial;

////////////////////////////////////////////////////////////////////////////
// impl

half ComputeVolumetricFogInternal(in float3 cameraToWorldPos)
{
	#define heightScale										vfParams.x
	#define volFogHeightDensityAtViewer		half(vfParams.y)
	#define fogDensity										half(vfParams.z)
	#define densityClamp									half(vfParams.w)

	half fogInt = 1.0h;

	float t = heightScale * cameraToWorldPos.z;
	if (abs(t) > 0.01f)
	{
#if PS3 // NOTE: can easily become #INF in following 'exp' instr, ok for other platforms
		t = min(t, 13.0f);
#endif
		fogInt *= (exp(t) - 1.0f) / t;
	}

	// NOTE: volFogHeightDensityAtViewer = log2(e) * fogDensity * exp(heightScale * vfViewPos.z + heightOffset);
	const half l = length(cameraToWorldPos);
	const half u = l * volFogHeightDensityAtViewer;
	fogInt *= u;

	half f = saturate(exp2(-fogInt));

	half r = saturate(l * vfRampParams.x + vfRampParams.y);
	r = r * (2 - r);
	r = r * vfRampParams.z + vfRampParams.w;

	f = (1 - f) * r;
	return max(1 - f, densityClamp);
}

// RET.xyz = fog color (HDR)
// RET.w = fog factor to lerp scene/object color with (i.e. lerp(RET.xyz, sceneColor.xyz, RET.w))
half4 GetVolumetricFogColorInternal(in float3 worldPos, in float3 cameraToWorldPos, in float radialFogShadowInfluence, in float ambientFogShadowInfluence)
{
	#define heightGradScale								vfColGradParams.x
	#define heightGradOffset							vfColGradParams.y
	#define radialSizeCtrl								vfColGradParams.z
	#define radialLobeCtrl								vfColGradParams.w

	#define radialColor										vfColGradRadial.xyz
	#define invZFar												vfColGradRadial.w

	const half fog = ComputeVolumetricFogInternal(cameraToWorldPos);

	half h = saturate(worldPos.z * heightGradScale + heightGradOffset);
	h = h * (2 - h);

	half3 fogColor = (vfColGradBase + h * vfColGradDelta) * ambientFogShadowInfluence;

	const half l = saturate(length(cameraToWorldPos) * invZFar);
	const half radialLobe = pow(l, radialLobeCtrl);
	const half radialSize = exp2(dot(normalize(cameraToWorldPos), vfSunDir) * -radialSizeCtrl + radialSizeCtrl); // exp2(-radialSizeCtrl * (1-cos(x))

	fogColor += radialLobe * radialSize * radialColor * radialFogShadowInfluence;

	return half4(fogColor, fog);
}

half4 GetVolumetricFogColorInternal(in float3 worldPos)
{
#ifdef %_VS
  vfViewPos.xyz = g_VS_WorldViewPos;
#endif
  float3 camToWorld = worldPos - vfViewPos.xyz;
  return GetVolumetricFogColorInternal(worldPos, camToWorld, 1, 1);
}


////////////////////////////////////////////////////////////////////////////
// delegates

half4 GetVolumetricFogColor(in float3 worldPos, in float3 cameraToWorldPos, in float radialFogShadowInfluence, in float ambientFogShadowInfluence)
{
	return GetVolumetricFogColorInternal(worldPos, cameraToWorldPos, radialFogShadowInfluence, ambientFogShadowInfluence);
}


half4 GetVolumetricFogColor(in float3 worldPos, in float3 cameraToWorldPos)
{
	return GetVolumetricFogColorInternal(worldPos, cameraToWorldPos, 1, 1);
}

half4 GetVolumetricFogColor(in float3 worldPos)
{
	return GetVolumetricFogColorInternal(worldPos);
}

half GetVolumetricFogDensity(in float3 worldPos)
{
#ifdef %_VS
  vfViewPos.xyz = g_VS_WorldViewPos;
#endif
  float3 camToWorld = worldPos - vfViewPos.xyz;
  return ComputeVolumetricFogInternal(camToWorld);
}
